// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "coin/proto/coin_order_enums.pb.h"
#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/order/parser.h"
#include "coin2/exchange/upbit/api_util/order_manager.h"

namespace coin2::exchange::upbit::order_v1 {

class UpbitParser : public base::order::BaseParser {
  using JsonValue = base::order::BaseParser::JsonValue;
  using SubmitOrderResponse = base::order::BaseParser::SubmitOrderResponse;
  using ProductOrderBundle = base::order::BaseParser::ProductOrderBundle;
  using ProductFillBundle = base::order::BaseParser::ProductFillBundle;
  using AccountBalance = base::order::BaseParser::AccountBalance;
  using ProductOrderElement = base::order::BaseParser::ProductOrderElement;
  using AccountPosition = base::order::BaseParser::AccountPosition;
  using OrderErrorCode = base::order::BaseParser::OrderErrorCode;
  using OrderSubTopicId = base::order::BaseParser::OrderSubTopicId;
  using OrderSide = coin::proto::OrderSide;
  using OrderType = coin::proto::OrderType;
  using OrderCacheManager = coin2::exchange::upbit::api_util::OrderManager;

 public:
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  AccountBalance ParseBalance(const JsonValue& document) override;
  bool ParseRawBalance(const JsonValue& document, RawBalanceMap* raw_balance_map);
  void SetOrderCacheManager(OrderCacheManager* order_mgr) { order_mgr_ = order_mgr; }

 private:
  void ParseSubmitResponse();
  void ParseCancelResponse();
  void ParseRestAllOrders();
  void ParseRestOpenOrders();
  void ParseRestFillOrders();
  void ParseRestAccountBalance();
  void ParseWsMessage();

  void ParseRestOrderInfoSingle(const JsonDocument& document);
  void ParseRestFillInfoList(const JsonDocument& document);
  void ParseRestOrderInfo(const JsonValue& data, ProductOrderElement* product_order_element);
  bool ParseRestFillInfo(const JsonValue& data, ProductFillBundle* product_fill_bundle);
  bool ParseRestFillInfoV2(const JsonValue& data, ProductFillBundle* product_fill_bundle);

  bool IsUpbitErrMessage(const JsonValue& data) const;
  int64_t GetProcOrderId(const JsonValue& data) const;
  int64_t GetTimestamp(std::string_view time_str) const;
  OrderSide GetOrderSide(std::string_view side) const;
  OrderType GetOrderType(std::string_view type) const;
  void SetOrderStatus(const JsonValue& data, ProductOrderElement* product_order_element) const;

  const std::unordered_set<std::string>& native_symbols() {
    static std::unordered_set<std::string> native_symbols = [&]() -> auto {
      std::unordered_set<std::string> set;
      for (const auto* product : symcache_->GetProducts()) {
        set.emplace(product->order_native_symbol());
      }
      return set;
    }
    ();
    return native_symbols;
  }

 private:
  OrderCacheManager* order_mgr_ = nullptr;
  bool is_account_balance_initialized_ = false;

  // static constants
  static const MarketExchangeApi UPBIT_KR;
  static const MarketExchangeApi UPBIT_ID;
  static const MarketExchangeApi UPBIT_SG;
  static const MarketExchangeApi UPBIT_TH;
};

}  // namespace coin2::exchange::upbit::order_v1
